package flag

import (
	"bytes"
	"fmt"
	"github.com/pkg/sftp"
	"golang.org/x/crypto/ssh"
	"io"
	"log"
	"os"
	"path/filepath"
	"server/global"
	"server/model/appTypes"
	time2 "time"
)

func SQLExport() error {
	mysqlCfg := global.Config.Mysql

	timer := time2.Now().Format("20060102")
	sqlPath := fmt.Sprintf("mysql_%s", timer)
	ssh := appTypes.SSH{
		SSHHost:        mysqlCfg.Host,
		SSHPort:        22,
		SSHUser:        mysqlCfg.Username,
		SSHPassword:    mysqlCfg.Password,
		SSHKeyPath:     "",
		MySQLContainer: "mysql",
		MySQLUser:      "root",
		MySQLPassword:  mysqlCfg.Password,
		Database:       mysqlCfg.DBName,
		LocalSaveDir:   sqlPath,
	}

	// 执行导出
	err := exportMySQLViaSSH(ssh)
	if err != nil {
		log.Fatalf("导出失败: %v", err)
	}
	log.Println("数据库导出成功!")
	return nil
}

// exportMySQLViaSSH 通过SSH执行远程导出
func exportMySQLViaSSH(ssh appTypes.SSH) error {
	// 1. 建立SSH连接
	sshClient, err := createSSHClient(ssh)
	if err != nil {
		return fmt.Errorf("SSH连接失败: %w", err)
	}
	defer sshClient.Close()

	// 2. 创建SFTP客户端用于文件传输
	sftpClient, err := sftp.NewClient(sshClient)
	if err != nil {
		return fmt.Errorf("创建SFTP客户端失败: %w", err)
	}
	defer sftpClient.Close()

	// 3. 在远程主机上执行mysqldump
	log.Printf("在远程主机上执行mysqldump...")
	err = executeRemoteDump(sshClient, ssh, ssh.LocalSaveDir)
	if err != nil {
		return fmt.Errorf("远程执行mysqldump失败: %w", err)
	}

	// 4. 下载导出的文件到本地
	localFileName := filepath.Base(ssh.LocalSaveDir + ".sql")
	localFilePath := filepath.Join(ssh.LocalSaveDir, localFileName)

	log.Printf("下载文件 %s 到本地 %s...", ssh.LocalSaveDir, localFilePath)
	err = downloadFile(sftpClient, ssh.LocalSaveDir, localFilePath)
	if err != nil {
		return fmt.Errorf("下载文件失败: %w", err)
	}

	// 5. 清理远程临时文件
	log.Printf("清理远程临时文件...")
	err = cleanupRemoteFile(sshClient, ssh.LocalSaveDir)
	if err != nil {
		log.Printf("警告: 清理远程文件失败: %v", err)
	}

	return nil
}

// createSSHClient 创建SSH客户端连接
func createSSHClient(ssh1 appTypes.SSH) (*ssh.Client, error) {
	// 认证方法优先顺序: 私钥 > 密码
	var authMethods []ssh.AuthMethod

	// 尝试使用私钥认证
	if ssh1.SSHKeyPath != "" {
		key, err := os.ReadFile(ssh1.SSHKeyPath)
		if err == nil {
			signer, err := ssh.ParsePrivateKey(key)
			if err == nil {
				authMethods = append(authMethods, ssh.PublicKeys(signer))
			}
		}
	}

	// 如果配置了密码，添加密码认证
	if ssh1.SSHPassword != "" {
		authMethods = append(authMethods, ssh.Password(ssh1.SSHPassword))
	}

	if len(authMethods) == 0 {
		return nil, fmt.Errorf("没有可用的SSH认证方法")
	}

	sshConfig := &ssh.ClientConfig{
		User:            ssh1.SSHUser,
		Auth:            authMethods,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(), // 生产环境应验证主机密钥
		Timeout:         30 * time2.Second,
	}

	// 连接SSH服务器
	addr := fmt.Sprintf("%s:%d", ssh1.SSHHost, ssh1.SSHPort)
	client, err := ssh.Dial("tcp", addr, sshConfig)
	if err != nil {
		return nil, fmt.Errorf("SSH连接失败: %w", err)
	}

	return client, nil
}

// executeRemoteDump 在远程主机上执行mysqldump
func executeRemoteDump(client *ssh.Client, ssh appTypes.SSH, outputPath string) error {
	// 构建mysqldump命令
	dumpCmd := fmt.Sprintf(
		"docker exec %s mysqldump -u%s -p%s %s > %s",
		ssh.MySQLContainer,
		ssh.MySQLUser,
		ssh.MySQLPassword,
		ssh.Database,
		outputPath,
	)

	// 创建SSH会话
	session, err := client.NewSession()
	if err != nil {
		return fmt.Errorf("创建SSH会话失败: %w", err)
	}
	defer session.Close()

	// 配置会话输出
	var stdout, stderr bytes.Buffer
	session.Stdout = &stdout
	session.Stderr = &stderr

	// 执行命令
	err = session.Run(dumpCmd)
	if err != nil {
		return fmt.Errorf("命令执行失败: %w\n%s", err, stderr.String())
	}

	return nil
}

// downloadFile 通过SFTP下载文件
func downloadFile(sftpClient *sftp.Client, remotePath, localPath string) error {
	// 确保本地目录存在
	if err := os.MkdirAll(filepath.Dir(localPath), 0755); err != nil {
		return fmt.Errorf("创建本地目录失败: %w", err)
	}

	// 打开远程文件
	srcFile, err := sftpClient.Open(remotePath)
	if err != nil {
		return fmt.Errorf("打开远程文件失败: %w", err)
	}
	defer srcFile.Close()

	// 创建本地文件
	dstFile, err := os.Create(localPath)
	if err != nil {
		return fmt.Errorf("创建本地文件失败: %w", err)
	}
	defer dstFile.Close()

	// 复制文件内容
	_, err = io.Copy(dstFile, srcFile)
	if err != nil {
		return fmt.Errorf("文件复制失败: %w", err)
	}

	return nil
}

// cleanupRemoteFile 清理远程临时文件
func cleanupRemoteFile(client *ssh.Client, filePath string) error {
	session, err := client.NewSession()
	if err != nil {
		return fmt.Errorf("创建清理会话失败: %w", err)
	}
	defer session.Close()

	// 执行删除命令
	cmd := fmt.Sprintf("rm -f %s", filePath)
	if err := session.Run(cmd); err != nil {
		return fmt.Errorf("删除远程文件失败: %w", err)
	}

	return nil
}
